#include "../include/command.h"
#pragma warning(disable:4996)
#ifdef __linux__
#include <unistd.h>
#else
#include <windows.h>
#endif

extern qword iregs[128];
extern float fregs[64];
extern double dregs[64];
byte codes[0x1000000];
byte datas[0x1000000];

dword loader()
{
	dword code;
	memcpy(&code, (codes + iregs[Code]), 4);
	iregs[Code] += 4;
	return code;
}
void selector(dword code)
{
	switch (code >> 29)
	{
	case Spec:
		TypeS(*(CodeS *)(&code));
		break;
	case Regs:
		TypeR(*(CodeR *)(&code));
		break;
	case Load:
		TypeL(*(CodeL *)(&code));
		break;
	case Imme:
		TypeI(*(CodeI *)(&code));
		break;
	case Float:
		TypeF(*(CodeF *)(&code));
		break;
	default:
		std::cout << "Error:Unknown Instruction Type.\n";
		FILE *output = fopen("error.bin", "wb");
		fwrite(codes + iregs[Code] - 512, sizeof(byte), 1024, output);
		fwrite(datas, sizeof(byte), 1024, output);
		fwrite(iregs, sizeof(qword), 128, output);
		fwrite(fregs, sizeof(float), 64, output);
		fwrite(dregs, sizeof(double), 64, output);
		exit(2);
		break;
	}
}
void TypeS(CodeS code)
{
	switch (code.name & 0x1f)
	{
	case 0x00:
#ifdef __linux__
		usleep(0x10);
#else
		;
#endif
		break;
	case 0x01:
		memcpy(datas + (iregs[Stack] - 8), iregs + code.arg1, 8);
		iregs[Stack] -= 8;
		break;
	case 0x02:
		memcpy(iregs + code.arg1, datas + iregs[Stack], 8);
		iregs[Stack] += 8;
		break;
	case 0x03:
		switch (code.arg1)
		{
		case 0x00:
			for (word i = 0; i < code.arg2; i++)
				datas[iregs[Index] + i] = getchar();
			break;
		case 0x01:
			for (word i = 0; i < code.arg2; i++)
				putchar(datas[iregs[Index] + i]);
			break;
		case 0x02:
		{
			std::ifstream inFile(std::string((char *)(datas + (iregs[Base] & 0xffffff)), (iregs[Base] >> 32) & 0xff).c_str(), std::ios::binary);
			if (inFile.is_open())
				for (int i = 0; i < code.arg2; i++)
					inFile >> datas[iregs[Index] + i];
			inFile.close();
		}
		break;
		case 0x03:
		{
			std::ofstream outFile(std::string((char *)(datas + (iregs[Base] & 0xffffff)), (iregs[Base] >> 32) & 0xff).c_str(), std::ios::binary);
			if (outFile.is_open())
				for (int i = 0; i < code.arg2; i++)
					outFile << datas[iregs[Index] + i];
			outFile.close();
		}
		break;
		}
		break;
	case 0x04:
		memcpy(iregs + Code, datas + iregs[Stack], 8);
		memcpy(iregs + Base, datas + iregs[Stack] + 8, 8);
		iregs[Stack] += 16;
		break;
	case 0x05:
		iregs[Flag] &= ~(RUNNING);
	default:
		std::cout << "Error:Unknown Instruction.\n";
		FILE *output = fopen("error.bin", "wb");
		fwrite(codes + iregs[Code] - 512, sizeof(byte), 1024, output);
		fwrite(datas, sizeof(byte), 1024, output);
		fwrite(iregs, sizeof(qword), 128, output);
		fwrite(fregs, sizeof(float), 64, output);
		fwrite(dregs, sizeof(double), 64, output);
		exit(2);
		break;
	}
}
void TypeR(CodeR code)
{
	switch (code.name & 0x1f)
	{
	case 0x00:
		iregs[code.rd & 0x7f] = iregs[code.r1 & 0x7f] + iregs[code.r2 & 0x7f];
		changeFlags(code.rd);
		break;
	case 0x01:
		iregs[code.rd & 0x7f] = iregs[code.r1 & 0x7f] - iregs[code.r2 & 0x7f];
		changeFlags(code.rd);
		break;
	case 0x02:
		iregs[code.rd & 0x7f] = iregs[code.r1 & 0x7f] * iregs[code.r2 & 0x7f];
		changeFlags(code.rd);
		break;
	case 0x03:
		iregs[code.rd & 0x7f] = iregs[code.r1 & 0x7f] / iregs[code.r2 & 0x7f];
		changeFlags(code.rd);
		break;
	case 0x04:
		iregs[code.rd & 0x7f] = iregs[code.r1] & 0x7f % iregs[code.r2 & 0x7f];
		break;
	case 0x09:
		iregs[code.r1 & 0x7f] = iregs[code.r2 & 0x7f];
		break;
	case 0x10:
		iregs[code.rd & 0x7f] = iregs[code.r1 & 0x7f] & iregs[code.r2 & 0x7f];
		changeFlags(code.rd);
		break;
	case 0x11:
		iregs[code.rd & 0x7f] = iregs[code.r1 & 0x7f] | iregs[code.r2 & 0x7f];
		changeFlags(code.rd);
		break;
	case 0x12:
		iregs[code.rd & 0x7f] = iregs[code.r1 & 0x7f] ^ iregs[code.r2 & 0x7f];
		changeFlags(code.rd);
		break;
	case 0x13:
		iregs[code.rd & 0x7f] = ~iregs[code.rd & 0x7f];
		break;
	case 0x14:
		iregs[code.rd & 0x7f] = iregs[code.r1 & 0x7f] << (code.r2 & 0x3f);
		break;
	case 0x15:
		iregs[code.rd & 0x7f] = iregs[code.r1 & 0x7f] >> (code.r2 & 0x3f);
		break;
	case 0x16:
		iregs[0x7f] = iregs[code.r1 & 0x7f] - iregs[code.r2 & 0x7f];
		changeFlags(0x7f);
		break;
	default:
		std::cout << "Error:Unknown Instruction.\n";
		FILE *output = fopen("error.bin", "wb");
		fwrite(codes + iregs[Code] - 512, sizeof(byte), 1024, output);
		fwrite(datas, sizeof(byte), 1024, output);
		fwrite(iregs, sizeof(qword), 128, output);
		fwrite(fregs, sizeof(float), 64, output);
		fwrite(dregs, sizeof(double), 64, output);
		exit(2);
		break;
	}
}
void TypeL(CodeL code)
{
	switch (code.name & 0x1f)
	{
	case 0x00:
		iregs[code.regd & 0x7f] = *(datas + (iregs[Data] + code.address));
		break;
	case 0x01:
		memcpy(iregs + code.regd, datas + iregs[Data] + code.address, 2);
		break;
	case 0x02:
		memcpy(iregs + code.regd, datas + iregs[Data] + code.address, 4);
		break;
	case 0x03:
		memcpy(iregs + code.regd, datas + iregs[Data] + code.address, 8);
		break;
	case 0x08:
		*(datas + (iregs[Data] + code.address)) = (byte)(iregs[code.regd & 0x7f]);
		break;
	case 0x09:
		memcpy(datas + iregs[Data] + code.address, iregs + code.regd, 2);
		break;
	case 0x0a:
		memcpy(datas + iregs[Data] + code.address, iregs + code.regd, 4);
		break;
	case 0x0b:
		memcpy(datas + iregs[Data] + code.address, iregs + code.regd, 8);
		break;
	default:
		std::cout << "Error:Unknown Instruction.\n";
		FILE *output = fopen("error.bin", "wb");
		fwrite(codes + iregs[Code] - 512, sizeof(byte), 1024, output);
		fwrite(datas, sizeof(byte), 1024, output);
		fwrite(iregs, sizeof(qword), 128, output);
		fwrite(fregs, sizeof(float), 64, output);
		fwrite(dregs, sizeof(double), 64, output);
		exit(2);
		break;
	}
}
void TypeI(CodeI code)
{
	switch (code.name & 0x1f)
	{
	case 0x00:
		iregs[Code] = ((qword)(code.addr_h) << 16) | code.addr_l;
		break;
	case 0x01:
		if (iregs[Flag] & ZERO)
			iregs[Code] = ((qword)(code.addr_h) << 16) | code.addr_l;
		break;
	case 0x02:
		if (iregs[Flag] & POSITIVE)
			iregs[Code] = ((qword)(code.addr_h) << 16) | code.addr_l;
		break;
	case 0x03:
		if (iregs[Flag] & (ZERO | POSITIVE))
			iregs[Code] = ((qword)(code.addr_h) << 16) | code.addr_l;
		break;
	case 0x04:
		if (iregs[Flag] & NEGATIVE)
			iregs[Code] = ((qword)(code.addr_h) << 16) | code.addr_l;
		break;
	case 0x05:
		if (iregs[Flag] & (ZERO | NEGATIVE))
			iregs[Code] = ((qword)(code.addr_h) << 16) | code.addr_l;
		break;
	case 0x06:
		if (iregs[Flag] & (NEGATIVE | POSITIVE))
			iregs[Code] = ((qword)(code.addr_h) << 16) | code.addr_l;
		break;
	case 0x07:
		memcpy(datas + iregs[Stack], iregs + Base, sizeof(qword));
		memcpy(datas + iregs[Stack] + 8, iregs + Code, sizeof(qword));
		iregs[Stack] += 16;
		iregs[Code] = ((qword)(code.addr_h) << 16) | code.addr_l;
		break;
	default:
		std::cout << "Error:Unknown Instruction.\n";
		FILE *output = fopen("error.bin", "wb");
		fwrite(codes + iregs[Code] - 512, sizeof(byte), 1024, output);
		fwrite(datas, sizeof(byte), 1024, output);
		fwrite(iregs, sizeof(qword), 128, output);
		fwrite(fregs, sizeof(float), 64, output);
		fwrite(dregs, sizeof(double), 64, output);
		exit(2);
		break;
	}
}
void TypeF(CodeF code)
{
	byte pointer = 0;
	switch (code.name & 0x1f)
	{
	case 0x00:
		if ((iregs[RCounts] & 0xff) > 63)
		{
			std::cout << "Error:Float Stack Overflow.\n";
			FILE *output = fopen("error.bin", "wb");
			fwrite(codes + iregs[Code] - 512, sizeof(byte), 1024, output);
			fwrite(datas, sizeof(byte), 1024, output);
			fwrite(iregs, sizeof(qword), 128, output);
			fwrite(fregs, sizeof(float), 64, output);
			fwrite(dregs, sizeof(double), 64, output);
			exit(2);
		}
		memcpy(fregs + (iregs[RCounts] & 0x3f), datas + ((qword)(code.argH) << 16) + code.argL, sizeof(float));
		iregs[RCounts] += 1;
		break;
	case 0x01:
		if ((iregs[RCounts] & 0xff) == 0)
		{
			std::cout << "Error:Float Stack Underflow.\n";
			FILE *output = fopen("error.bin", "wb");
			fwrite(codes + iregs[Code] - 512, sizeof(byte), 1024, output);
			fwrite(datas, sizeof(byte), 1024, output);
			fwrite(iregs, sizeof(qword), 128, output);
			fwrite(fregs, sizeof(float), 64, output);
			fwrite(dregs, sizeof(double), 64, output);
			exit(2);
		}
		memcpy(datas + ((qword)(code.argH) << 16) + code.argL, fregs + (iregs[RCounts] & 0x3f), sizeof(float));
		iregs[RCounts] -= 1;
		break;
	case 0x02:
		if ((iregs[RCounts] & 0xff) < 2)
		{
			std::cout << "Error:Float Stack Underflow.\n";
			FILE *output = fopen("error.bin", "wb");
			fwrite(codes + iregs[Code] - 512, sizeof(byte), 1024, output);
			fwrite(datas, sizeof(byte), 1024, output);
			fwrite(iregs, sizeof(qword), 128, output);
			fwrite(fregs, sizeof(float), 64, output);
			fwrite(dregs, sizeof(double), 64, output);
			exit(2);
		}
		pointer = iregs[RCounts] & 0x3f;
		switch (code.argH)
		{
		case 0x00:
			fregs[pointer - 1] = fregs[pointer] + fregs[pointer - 1];
			iregs[RCounts] -= 1;
			break;
		case 0x01:
			fregs[pointer - 1] = fregs[pointer] - fregs[pointer - 1];
			iregs[RCounts] -= 1;
			break;
		case 0x02:
			fregs[pointer - 1] = fregs[pointer] * fregs[pointer - 1];
			iregs[RCounts] -= 1;
			break;
		case 0x03:
			fregs[pointer - 1] = fregs[pointer] / fregs[pointer - 1];
			iregs[RCounts] -= 1;
			break;
		}
		break;
	case 0x08:
		iregs[0x7f] = static_cast<qword>(fregs[iregs[RCounts] & 0x3f]);
		break;
	case 0x09:
		dregs[(iregs[RCounts] >> 8) & 0x3f] = (double)fregs[iregs[RCounts] & 0x3f];
		iregs[RCounts] += 0x100;
		break;
	case 0x10:
		if ((iregs[RCounts] & 0xff00) > 6300)
		{
			std::cout << "Error:Float Stack Overflow.\n";
			FILE *output = fopen("error.bin", "wb");
			fwrite(codes + iregs[Code] - 512, sizeof(byte), 1024, output);
			fwrite(datas, sizeof(byte), 1024, output);
			fwrite(iregs, sizeof(qword), 128, output);
			fwrite(fregs, sizeof(float), 64, output);
			fwrite(dregs, sizeof(double), 64, output);
			exit(2);
		}
		memcpy(fregs + ((iregs[RCounts] >> 8) & 0x3f), datas + ((qword)(code.argH) << 16) + code.argL, sizeof(float));
		iregs[RCounts] += 0x100;
		break;
	case 0x11:
		if ((iregs[RCounts] & 0xff00) == 0)
		{
			std::cout << "Error:Float Stack Underflow.\n";
			FILE *output = fopen("error.bin", "wb");
			fwrite(codes + iregs[Code] - 512, sizeof(byte), 1024, output);
			fwrite(datas, sizeof(byte), 1024, output);
			fwrite(iregs, sizeof(qword), 128, output);
			fwrite(fregs, sizeof(float), 64, output);
			fwrite(dregs, sizeof(double), 64, output);
			exit(2);
		}
		memcpy(datas + ((qword)(code.argH) << 16) + code.argL, fregs + ((iregs[RCounts] >> 8) & 0x3f), sizeof(float));
		iregs[RCounts] -= 0x100;
		break;
	case 0x12:
		if ((iregs[RCounts] & 0xff00) < 2)
		{
			std::cout << "Error:Float Stack Underflow.\n";
			FILE *output = fopen("error.bin", "wb");
			fwrite(codes + iregs[Code] - 512, sizeof(byte), 1024, output);
			fwrite(datas, sizeof(byte), 1024, output);
			fwrite(iregs, sizeof(qword), 128, output);
			fwrite(fregs, sizeof(float), 64, output);
			fwrite(dregs, sizeof(double), 64, output);
			exit(2);
		}
		pointer = (iregs[RCounts] >> 8) & 0x3f;
		switch (code.argH)
		{
		case 0x00:
			dregs[pointer - 1] = dregs[pointer] + dregs[pointer - 1];
			iregs[RCounts] -= 0x100;
			break;
		case 0x01:
			dregs[pointer - 1] = dregs[pointer] - dregs[pointer - 1];
			iregs[RCounts] -= 0x100;
			break;
		case 0x02:
			dregs[pointer - 1] = dregs[pointer] * dregs[pointer - 1];
			iregs[RCounts] -= 0x100;
			break;
		case 0x03:
			dregs[pointer - 1] = dregs[pointer] / dregs[pointer - 1];
			iregs[RCounts] -= 0x100;
			break;
		}
		break;
	case 0x18:
		iregs[0x7f] = static_cast<qword>(dregs[(iregs[RCounts] >> 8) & 0x3f]);
		break;
	case 0x19:
		fregs[iregs[RCounts] & 0x3f] = (float)dregs[(iregs[RCounts] >> 8) & 0x3f];
		iregs[RCounts] += 1;
		break;
	default:
		std::cout << "Error:Unknown Instruction.\n";
		FILE *output = fopen("error.bin", "wb");
		fwrite(codes + iregs[Code] - 512, sizeof(byte), 1024, output);
		fwrite(datas, sizeof(byte), 1024, output);
		fwrite(iregs, sizeof(qword), 128, output);
		fwrite(fregs, sizeof(float), 64, output);
		fwrite(dregs, sizeof(double), 64, output);
		exit(2);
		break;
	}
}